<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.2" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="网络编程," />





  <link rel="alternate" href="/atom.xml" title="Allen.L Notes" type="application/atom+xml" />




  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.2" />






<meta name="description" content="一.设备内核对象原理：程序每隔一段时间，轮询检测设备内核对象是否处于激发状态。 缺点：  当没有事件完成时，设备内核对象处于未激活状态。但程序还是会一直轮询无效的代码指令，降低CPU效率。 程序每轮询一次，Sleep函数睡眠，sleep睡眠越长，造成数据响应越不及时。 睡眠时间越短，虽然响应变快，但是轮询次数增多，无效轮询指令执行越多。如果睡眠时间越长，轮询次数越少，但响应变慢。  二.事件内核对">
<meta name="keywords" content="网络编程">
<meta property="og:type" content="article">
<meta property="og:title" content="异步网络通信模型">
<meta property="og:url" content="https://lp112124.github.io/2017/08/17/异步网络通信模型/index.html">
<meta property="og:site_name" content="Allen.L Notes">
<meta property="og:description" content="一.设备内核对象原理：程序每隔一段时间，轮询检测设备内核对象是否处于激发状态。 缺点：  当没有事件完成时，设备内核对象处于未激活状态。但程序还是会一直轮询无效的代码指令，降低CPU效率。 程序每轮询一次，Sleep函数睡眠，sleep睡眠越长，造成数据响应越不及时。 睡眠时间越短，虽然响应变快，但是轮询次数增多，无效轮询指令执行越多。如果睡眠时间越长，轮询次数越少，但响应变慢。  二.事件内核对">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2017-09-27T21:56:10.083Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="异步网络通信模型">
<meta name="twitter:description" content="一.设备内核对象原理：程序每隔一段时间，轮询检测设备内核对象是否处于激发状态。 缺点：  当没有事件完成时，设备内核对象处于未激活状态。但程序还是会一直轮询无效的代码指令，降低CPU效率。 程序每轮询一次，Sleep函数睡眠，sleep睡眠越长，造成数据响应越不及时。 睡眠时间越短，虽然响应变快，但是轮询次数增多，无效轮询指令执行越多。如果睡眠时间越长，轮询次数越少，但响应变慢。  二.事件内核对">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    sidebar: {"position":"left","display":"post","offset":12,"offset_float":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://lp112124.github.io/2017/08/17/异步网络通信模型/"/>





  <title>异步网络通信模型 | Allen.L Notes</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Allen.L Notes</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">Quick notes</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://lp112124.github.io/2017/08/17/异步网络通信模型/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Allen.L">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Allen.L Notes">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">异步网络通信模型</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-08-17T11:59:20-04:00">
                2017-08-17
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h3 id="一-设备内核对象"><a href="#一-设备内核对象" class="headerlink" title="一.设备内核对象"></a>一.设备内核对象</h3><p><strong>原理</strong>：<br>程序每隔一段时间，轮询检测设备内核对象是否处于激发状态。</p>
<p><strong>缺点</strong>：</p>
<ol>
<li>当没有事件完成时，设备内核对象处于未激活状态。但程序还是会一直轮询无效的代码指令，降低CPU效率。</li>
<li>程序每轮询一次，Sleep函数睡眠，sleep睡眠越长，造成数据响应越不及时。</li>
<li>睡眠时间越短，虽然响应变快，但是轮询次数增多，无效轮询指令执行越多。如果睡眠时间越长，轮询次数越少，但响应变慢。</li>
</ol>
<h3 id="二-事件内核对象"><a href="#二-事件内核对象" class="headerlink" title="二.事件内核对象"></a>二.事件内核对象</h3><p><strong>原理</strong>：<br>设备内核对象与事件内核对象绑定，当设备内核对象完成任务处于激发状态，立即激活事件内核对象，结束WaitformultipleObjects阻塞状态，并进行相应处理。内部实现响应事件对象数组与socket数组一一对应，通过WAITFORMULTIPLEOBJECTS函数的返回值减去WAIT_OBJECT_0得到相应的SOCKET进行处理。</p>
<p><strong>优点</strong>：</p>
<ol>
<li>相对于设备内核对象模式，事件完成直接通知 ，不需要轮询，就不需要执行无效的轮询指令。</li>
<li>可以直接找到对应完成任务的SOCKET对象进行处理，不需要查找哪一个SOCKET完成。</li>
</ol>
<p><strong>缺点</strong>：<br>调用函数WAITFORMULTIPLEOBJECTS，而成员变量对象数组最大只能存储64个事件对象。</p>
<blockquote>
<p>两种方式解决：</p>
<blockquote>
<ol>
<li>可以在一个线程中多次调用函数，要运用超时判定执行，这个响应时间慢。</li>
<li>程序中建立多个线程，每个线程只有一个waitformultipleobjects函数。当需要处理的socket很多时，线程数会过多，CPU频繁切换线程，执行效率降低，响应变慢，用户体验差。</li>
</ol>
</blockquote>
</blockquote>
<h3 id="三-完成例程（APC）"><a href="#三-完成例程（APC）" class="headerlink" title="三.完成例程（APC）"></a>三.完成例程（APC）</h3><p><strong>原理</strong>：<br>哪个线程投递任务那么任务完成后也由该线程自动调用回调函数处理。</p>
<p><strong>优点</strong>：<br>在处理业务数据量小的情况下，效率非常高，不需要轮询，也不需要事件通知，当事件完成时，直接通过回调函数执行，解决了完成事件模型的64个事件内核对象数量限制。</p>
<p><strong>缺点</strong>：</p>
<ol>
<li>线程必须也只能处理自己投递的任务，完成任务后不能跨线程分派，会造成线程负载不均衡。某个线程投递的任务越多，完成后处理的任务也越多，该线程越忙；反之，投递任务越少的线程，相应处理的任务也越少，线程越清闲。</li>
<li>当投递任务的线程达到一定的数量时且任务繁重的情况下，那么忙碌线程就越多，会造成CPU切换线程次数增多，导致CPU大量执行切换指令，CPU效率严重降低。</li>
</ol>
<h3 id="四-完成端口（IOCP）"><a href="#四-完成端口（IOCP）" class="headerlink" title="四.完成端口（IOCP）"></a>四.完成端口（IOCP）</h3><p><strong>原理</strong>:<br>异步I/O（AIO），预先开好一定数量的线程，放置在闲队列中，在内部维护一个用户请求的消息队列，当有用户请求来到时，线程就排队逐一从消息队列中取出消息并加以处理。</p>
<p><strong>优点</strong>：</p>
<ol>
<li>完成端口会预先开好一定数量的线程，这样避免了每次请求去创建线程以及任务完成销毁线程所需要的时间。线程数量是由你所需要完成的业务量以及并发量决定的。</li>
<li>完成端口是由内核调度的，不需要应用层去维护，内核维护了线程的闲队列和忙队列两种方式，当线程接到用户请求处于忙队列时，加载到CPU执行请求，当执行到I/O指令时，将会被切换到闲队列中，等待I/O指令的完成，CPU可以继续加载其他忙队列线程，这样避免了在I/O指令时占用CPU的周期，可以让CPU一直处于执行计算指令的状态，极大的提升了CPU的有效利用率。同时避免了当线程数量过多且都处于忙碌状态时，CPU不断进行线程的上下文切换，降低CPU的效率这种情况的发生。在LINUX系统下CPU时间片为8毫秒上下文切换大概有200条汇编指令，耗费0.5毫秒时间。这样CPU切换时间占工作时间的1/16。对于CPU，上下文切换非常消耗时间。因此我们应该尽量避免这种切换。</li>
<li>完成端口的线程数量的确定是当CPU处于闲置状态且忙队列线程数为0时，说明线程都处于I/O堵塞状态，这时没有多余的线程去接收用户请求，这就需要继续添加闲置线程。在你业务所需最大并发量的情况下，通过资源管理器查看CPU使用效率为100%时，那么将当前线程数-1就是最佳线程数。为什么要-1，是因为CPU利用率超过负荷时显示也为100%。-1是为了保护CPU的基础上最大的压榨CPU的利用率。</li>
<li>CPU的效率与CPU核数(T)和线程数(S)的相关。</li>
</ol>
<blockquote>
<p>一般有三种情况:①T &gt; S ②T = S ③T &lt; S</p>
<blockquote>
<ol>
<li>CPU利用率没有达到最大。不管线程多忙总有一个核处于闲置状态。</li>
<li>当线程都处于忙碌状态时，效率高，但当有线程执行I/O指令时，那么没有多余线程执行用户请求。这么线程就I/O堵塞在CPU内核，CPU闲置。</li>
<li>大于一个；2N~2N+2;一个SOCKET一个线程。</li>
</ol>
</blockquote>
</blockquote>
<ul>
<li>寄存器读写速度&gt;内存读写速度&gt;&gt;外设读写速度</li>
<li>因为外设的数据处理速度远远低于CPU，读写外设数据的I/O相关指令，执行时间远大于读写寄存器或内存的计算相关指令。</li>
<li>CPU读写外设数据的指令是IN和OUT。如果需要用IN和OUT指令在寄存器与外设之间读写数据时，因为外设读写数据速度极慢，并且IN和OUT指令工作在阻塞模式，造成CPU阻塞而闲置 ，严重降低CPU的使用效率。</li>
</ul>
<h3 id="五．overlapped（重叠结构）"><a href="#五．overlapped（重叠结构）" class="headerlink" title="五．overlapped（重叠结构）"></a>五．overlapped（重叠结构）</h3><p>重叠结构产生的目的是避免同步模式下阻塞在I/O操作的时间，重叠I/O模式不需要CPU阻塞在I/O指令，而是立即执行后续指令，同时利用DMA技术来控制外设的I/O并行执行。相当于CPU原线程后续指令与I/O操作两条线重叠并行执行。使用重叠结构的应用程序在投递任务后，任务完成通知应用程序时，内部已将数据拷贝到应用层缓冲区，不需要经过CPU寄存器中转，节省了一次数据拷贝。为了提高CPU的执行效率，当系统执行I/O操作的时候，程序应该在执行I/O请求后，直接执行后续代码，与此同时外设在DMA控制下独立完成I/O数据传输等操作，当I/O操作完成之后，通知应用程序执行相应的I/O后续操作（包括已接收数据处理或I/O异常等处理）。在提交I/O请求和收到完成通知过程中就需要一种信息载体 一I/O OVERLAPPED重叠结构体，记录源数据内存地址，目的内存地址，传输字节数，完成后通知事件等信息。类似于快递订单，发货地址、商品名称和数量，收货地址，联系电话。</p>
<h3 id="六-DMA"><a href="#六-DMA" class="headerlink" title="六. DMA"></a>六. DMA</h3><p>DMA是接手CPU内存拷贝相关的I/O指令操作，没有DMA，CPU一样可以正常工作，但是有了DMA，可以接手CPU不适合也不擅长的低效I/O操作，释放了CPU 在I/O相关的操作。让CPU更专注更高效的完成计算相关操作。<br>在操作系统执行内存与外设之间处理数据拷贝的时候，只需要告诉DMA拷贝的起始地址、目标地址、拷贝字节数。然后CPU就可以继续执行后续代码，当完成拷贝工作之后DMA设备通过控制线通知CPU任务完成，进而形成操作系统的完成事件，完成事件触发应用程序中断CPU现行操作，处理数据传输结果。<br>DMA相当于一个专业执行IN和OUT指令的简版CPU（可以理解为单片机，有寄存器，总线，运算器，控制器），就是执行内存、外设相互间的数据循环拷贝。<br>DMA是指外部设备不通过CPU而直接与系统内存交换数据的接口技术。<br>系统总线（数据总线，控制总线，地址总线）是由CPU管理的，在DMA工作时，CPU将总线控制权转交（即CPU连接到总线的高阻状态），由DMA控制器接管。</p>
<blockquote>
<p>DMA控制器具有以下功能：</p>
<blockquote>
<ol>
<li>向CPU发送系统保持（HOLD）信号，提出总线接管请求。</li>
<li>当CPU发出允许接管信号后，负责对总线的控制，进入DMA模式。</li>
<li>能对存储器寻址及能修改地址指针，实现对内存的读写操作。</li>
<li>能决定本次DMA传送的字节数，判断的DMA传送是否结束。</li>
<li>发出DMA结束信号，使CPU恢复正常工作状态。</li>
</ol>
</blockquote>
</blockquote>
<p><strong>DMA 与CPU协调处理三种方式</strong>：</p>
<ol>
<li>停止CPU访问内存</li>
<li>周期挪用</li>
<li>DMA与CPU交替访问内存</li>
</ol>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/网络编程/" rel="tag"># 网络编程</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2017/08/16/构造与赋值函数调用问题/" rel="next" title="构造与赋值调用问题">
                <i class="fa fa-chevron-left"></i> 构造与赋值调用问题
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          
  <div class="comments" id="comments">
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/images/avatar.jpg"
               alt="Allen.L" />
          <p class="site-author-name" itemprop="name">Allen.L</p>
           
              <p class="site-description motion-element" itemprop="description">不积跬步，无以至千里。</p>
          
        </div>
        <nav class="site-state motion-element">

          
            <div class="site-state-item site-state-posts">
              <a href="/archives/">
                <span class="site-state-item-count">3</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          

          
            
            
            <div class="site-state-item site-state-tags">
              <a href="/tags/index.html">
                <span class="site-state-item-count">3</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        
          <div class="feed-link motion-element">
            <a href="/atom.xml" rel="alternate">
              <i class="fa fa-rss"></i>
              RSS
            </a>
          </div>
        

        <div class="links-of-author motion-element">
          
            
              <span class="links-of-author-item">
                <a href="https://github.com/lp112124" target="_blank" title="GitHub">
                  
                    <i class="fa fa-fw fa-github"></i>
                  
                    
                      GitHub
                    
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://stackoverflow.com/users/8426405/liu-allen" target="_blank" title="StackOverflow">
                  
                    <i class="fa fa-fw fa-stack-overflow"></i>
                  
                    
                      StackOverflow
                    
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://blog.csdn.net/lp112124" target="_blank" title="csdn">
                  
                    <i class="fa fa-fw fa-crosshairs"></i>
                  
                    
                      csdn
                    
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://segmentfault.com/u/lp112124" target="_blank" title="SegmentFault">
                  
                    <i class="fa fa-fw fa-meh-o"></i>
                  
                    
                      SegmentFault
                    
                </a>
              </span>
            
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#一-设备内核对象"><span class="nav-number">1.</span> <span class="nav-text">一.设备内核对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#二-事件内核对象"><span class="nav-number">2.</span> <span class="nav-text">二.事件内核对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#三-完成例程（APC）"><span class="nav-number">3.</span> <span class="nav-text">三.完成例程（APC）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#四-完成端口（IOCP）"><span class="nav-number">4.</span> <span class="nav-text">四.完成端口（IOCP）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#五．overlapped（重叠结构）"><span class="nav-number">5.</span> <span class="nav-text">五．overlapped（重叠结构）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#六-DMA"><span class="nav-number">6.</span> <span class="nav-text">六. DMA</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2017</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Allen.L</span>

  
</div>


  <div class="powered-by">
    由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
  </div>

  <span class="post-meta-divider">|</span>

  <div class="theme-info">
    主题 &mdash;
    <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
      NexT.Gemini
    </a>
  </div>


        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.2"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.2"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.2"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.2"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.2"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.2"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.2"></script>



  


  




	





  





  








  





  

  

  

  

  

  

</body>
</html>
